/**
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved.
 * Description:
 */
#include "plugins/ecmascript/runtime/base/builtins_base.h"
#include "plugins/ecmascript/runtime/internal_call_params.h"
#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h"
#include "plugins/ecmascript/runtime/js_promise.h"
#include "plugins/ecmascript/runtime/js_iterator.h"
#include "plugins/ecmascript/runtime/global_env.h"
#include "plugins/ecmascript/runtime/generator_helper.h"

namespace ark::ecmascript {

JSTaggedValue JSAsyncFromSyncIteratorValueUnwrapFunction::AsyncFromSyncIteratorValueUnwrapFunction(
    EcmaRuntimeCallInfo *argv)
{
    JSThread *thread = argv->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    // 1. Let F be the active function object.
    JSHandle<JSAsyncFromSyncIteratorValueUnwrapFunction> func(builtins_common::GetConstructor(argv));

    // 2. Return ! CreateIterResultObject(value, F.[[Done]]).
    JSHandle<JSTaggedValue> value = builtins_common::GetCallArg(argv, 0);
    return JSIterator::CreateIterResultObject(thread, value, func->GetDone().IsTrue()).GetTaggedValue();
}

JSTaggedValue JSAsyncFromSyncIteratorObject::CreateAsyncFromSyncIterator(JSThread *thread,
                                                                         const JSHandle<JSTaggedValue> &iterator,
                                                                         const JSHandle<JSTaggedValue> &nextMethod)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    // 1. Let asyncIterator be ! OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, « [[SyncIteratorRecord]] »).
    JSHandle<JSAsyncFromSyncIteratorObject> asyncIterator = factory->NewJSAsyncFromSyncIteratorObject();

    // 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord.
    asyncIterator->SetIterator(thread, iterator);
    asyncIterator->SetNextMethod(thread, nextMethod);

    // 3. Let nextMethod be ! Get(asyncIterator, "next").
    // 4. Let iteratorRecord be the Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
    // 5. Return iteratorRecord.
    return asyncIterator.GetTaggedValue();
}

JSTaggedValue JSAsyncFromSyncIteratorObject::AsyncFromSyncIteratorContinuation(
    JSThread *thread, const JSHandle<JSTaggedValue> &result, const JSHandle<PromiseCapability> &promiseCapability)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();

    // 1. Let done be IteratorComplete(result).
    JSHandle<JSTaggedValue> done(thread, JSTaggedValue(JSIterator::IteratorComplete(thread, result)));

    // 2. IfAbruptRejectPromise(done, promiseCapability).
    RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, done, promiseCapability);

    // 3. Let value be IteratorValue(result).
    JSHandle<JSTaggedValue> value = JSIterator::IteratorValue(thread, result);

    // 4. IfAbruptRejectPromise(value, promiseCapability).
    RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, value, promiseCapability);

    // 5. Let valueWrapper be PromiseResolve(%Promise%, value).
    JSHandle<JSTaggedValue> valueWrapper(
        thread, JSPromise::PromiseResolve(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()), value));

    // 6. IfAbruptRejectPromise(valueWrapper, promiseCapability).
    RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, valueWrapper, promiseCapability);

    // 7. 7. Let steps be the algorithm steps defined in Async-from-Sync Iterator Value Unwrap Functions.
    // 8. Let length be the number of non-optional parameters of the function definition in Async-from-Sync Iterator
    // Value Unwrap Functions.
    // 9. Let onFulfilled be ! CreateBuiltinFunction(steps, length, "", « [[Done]] »).
    JSHandle<JSAsyncFromSyncIteratorValueUnwrapFunction> onFulfilled =
        factory->NewJSAsyncFromSyncIteratorValueUnwrapFunction(reinterpret_cast<void *>(
            JSAsyncFromSyncIteratorValueUnwrapFunction::AsyncFromSyncIteratorValueUnwrapFunction));

    // 10. Set onFulfilled.[[Done]] to done.
    onFulfilled->SetDone(thread, done.GetTaggedValue());

    // 11. Perform ! PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).
    [[maybe_unused]] JSTaggedValue thenResult = builtins::promise::PerformPromiseThen(
        thread, JSHandle<JSPromise>::Cast(valueWrapper), JSHandle<JSTaggedValue>::Cast(onFulfilled),
        thread->GlobalConstants()->GetHandledUndefined(), JSHandle<JSTaggedValue>::Cast(promiseCapability));

    // 12. Return promiseCapability.[[Promise]].
    return promiseCapability->GetPromise();
}
}  // namespace ark::ecmascript
